﻿namespace Tests {

    #region Usings
    using Contoso.ActiveDirectory;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.Win32;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.DirectoryServices;
    using System.DirectoryServices.ActiveDirectory;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Security.AccessControl;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    #endregion

    [TestClass]
    public class UnitTests {

        #region Members
        private readonly string ComputersTestouDN;
        private readonly string GroupsTestOUDN;
        private readonly string UsersTestouDN;
        private readonly string TestouDN;
        private readonly string TestChildGroupDN;
        private readonly string TestChildGroupName;
        private readonly string TestComputerDN;
        private readonly string TestComputerName;
        private readonly string TestDomainName;
        private readonly string TestGroupDN;
        private readonly string TestGroupName;
        private readonly string TestUserDN;
        private readonly string TestUserName;
        #endregion

        #region Constructor
        public UnitTests() {
            TestDomainName = "CONTOSO";

            TestouDN = "OU=TSTOU,DC=contoso,DC=com";
            UsersTestouDN = "OU=Users,OU=TSTOU,DC=contoso,DC=com";
            GroupsTestOUDN = "OU=Groups,OU=TSTOU,DC=contoso,DC=com";
            ComputersTestouDN = "OU=Computers,OU=TSTOU,DC=contoso,DC=com";

            TestComputerName = "HDQABCDEFGPC001";
            TestComputerDN = string.Format("CN={0},{1}", TestComputerName, ComputersTestouDN);
            TestGroupName = "UGRP-TEST";
            TestGroupDN = string.Format("CN={0},{1}", TestGroupName, GroupsTestOUDN);
            TestChildGroupName = "GRP-TEST-CHILD";
            TestChildGroupDN = string.Format("CN={0},{1}", TestChildGroupName, GroupsTestOUDN);
            TestUserName = "jsmith001";
            TestUserDN = string.Format("CN={0},{1}", TestUserName, UsersTestouDN);

            var userDomain = Environment.GetEnvironmentVariable("USERDOMAIN");
            if (string.IsNullOrWhiteSpace(userDomain) || !string.Equals(userDomain, TestDomainName, StringComparison.OrdinalIgnoreCase)) {
                throw new ApplicationException("Check the domain where the tests are currently running, and update the TestDomainName property.");
            }
        }
        #endregion

        #region Private test helper methods

        private ComputerAccount GetTestComputer(string testComputerName = null) {

            var computerName = testComputerName;
            if (string.IsNullOrWhiteSpace(testComputerName)) {
                computerName = TestComputerName;
            }

            var testComputer = ADDataAccess.GetADObjectProperties<ComputerAccount>(
                TestComputerDN, ComputerAccount.AttributesToGet, ADDataAccess.QueryFilters.ComputerAccounts, PartitionScope.LocalPartition)
                .SingleOrDefault();

            if (testComputer == null) {
                var domainInfo = ADDataAccess.ADDomainInfo
                    .Where(x => string.Equals(x.NetbiosName, TestDomainName, StringComparison.OrdinalIgnoreCase))
                    .FirstOrDefault();

                if (domainInfo != null) {
                    var testComputerFilter = string.Format("(&(objectCategory=computer)(|(samAccountName={0})(cn={0})))", computerName);
                    testComputer = ADDataAccess.GetADObjectProperties<ComputerAccount>(
                        ADDataAccess.ForestRootDN, ComputerAccount.AttributesToGet, testComputerFilter, PartitionScope.LocalPartition)
                        .SingleOrDefault();
                }
            }

            if (testComputer != null) {
                ADDataAccess.Delete(testComputer.DN);
            }

            var computerPrincipal = ADDataAccess.ComputerAccountMethods.CreateComputerPrincipal(TestDomainName, computerName);
            if (computerPrincipal != null) {
                var parentDN = ADDataAccess.GetParentDN(computerPrincipal.DistinguishedName);
                if (!string.Equals(parentDN, ComputersTestouDN, StringComparison.OrdinalIgnoreCase)) {
                    ADDataAccess.MoveObject(computerPrincipal.DistinguishedName, ComputersTestouDN);
                }
            }

            testComputer = ADDataAccess.GetADObjectProperties<ComputerAccount>(
                TestComputerDN, ComputerAccount.AttributesToGet, ADDataAccess.QueryFilters.ComputerAccounts, PartitionScope.LocalPartition)
                .SingleOrDefault();

            Assert.IsNotNull(testComputer);
            Assert.IsTrue(testComputer.IsValid);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.CN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.DN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.DNSDomain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.Domain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.DomainPlusName));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.DomainDN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.ObjectCategory));
            Assert.IsTrue(testComputer.ObjectClass.Count > 0);
            Assert.IsTrue(testComputer.ObjectClass.Any(x => string.Equals(x, "computer", StringComparison.OrdinalIgnoreCase)));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.ParentDN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.SamAccountName));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.SamAccountNameFriendly));
            Assert.IsTrue(testComputer.SamAccountType.HasFlag(SamAccountType.SAM_MACHINE_ACCOUNT));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.TopLevelOU));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testComputer.TopLevelOUDN));
            Assert.IsTrue(testComputer.UserAccountControl.HasFlag(UserAccountControlFlags.WorkstationTrustAccount));
            Assert.IsTrue(testComputer.WhenChanged > DateTime.MinValue);
            Assert.IsTrue(testComputer.WhenChangedUTC > DateTime.MinValue);
            Assert.IsTrue(testComputer.WhenCreated > DateTime.MinValue);
            Assert.IsTrue(testComputer.WhenCreatedUTC > DateTime.MinValue);

            if (!testComputer.SamAccountName.EndsWith("$")) {
                var samAccountName = string.Format("{0}$", testComputer.SamAccountName);
                Assert.IsTrue(ADDataAccess.ComputerAccountMethods.IsComputerSamAccountNameValid(samAccountName));
                ADDataAccess.UpdateAttribute(testComputer.DN, "samAccountName", samAccountName);
            }

            return testComputer;
        }

        private GroupProperties GetTestGroup(string groupName, GroupTypeFlags groupTypeFlags) {

            if (string.IsNullOrWhiteSpace(groupName)) {
                groupName = TestGroupName;
            }
            else {
                var existingTestGroup = ADDataAccess.GetADObjectProperties<GroupProperties>(
                    "DC=contoso,DC=com", null, string.Format("samAccountName={0}", groupName), PartitionScope.LocalPartition)
                    .FirstOrDefault();
                if (existingTestGroup != null) {
                    return existingTestGroup;
                }
            }

            var testGroups = ADDataAccess.GetADObjectProperties<GroupProperties>(
                GroupsTestOUDN, GroupProperties.AttributesToGet, ADDataAccess.QueryFilters.GroupFilter, PartitionScope.LocalPartition)
                .ToList();

            var testGroup = testGroups
                .Where(x => string.Equals(x.Name, groupName))
                .SingleOrDefault();

            if (testGroup != null) {
                ADDataAccess.Delete(testGroup.DN);
            }

            ADDataAccess.GroupMethods.CreateGroup(GroupsTestOUDN, groupName, "Test Group", groupTypeFlags);
            testGroups = ADDataAccess.GetADObjectProperties<GroupProperties>(
                GroupsTestOUDN, GroupProperties.AttributesToGet, ADDataAccess.QueryFilters.GroupFilter, PartitionScope.LocalPartition)
                .ToList();

            Assert.IsTrue(testGroups.Count > 0);

            testGroup = testGroups
                .Where(x => string.Equals(x.Name, groupName))
                .SingleOrDefault();

            Assert.IsNotNull(testGroup);

            Assert.IsTrue(testGroup.GroupType.HasFlag(groupTypeFlags));

            #region Custom Attributes
            ADDataAccess.UpdateAttribute(testGroup.DN, "miExtensionAttribute1", "AAAAAAAAAAAAAAAA");
            ADDataAccess.UpdateAttribute(testGroup.DN, "miExtensionAttribute1", "BBBBBBBBBBBBBBBB");

            testGroup = testGroup.Refresh<GroupProperties>();

            Assert.IsTrue(testGroup.CustomAttributes.MIExtensionAttribute1.Any(x => string.Equals(x, "AAAAAAAAAAAAAAAA")));
            Assert.IsTrue(testGroup.CustomAttributes.MIExtensionAttribute1.Any(x => string.Equals(x, "BBBBBBBBBBBBBBBB")));
            #endregion

            return testGroup;
        }

        private UserAccount GetTestUser(string userName = null, string givenName = null, string surname = null) {

            if (string.IsNullOrWhiteSpace(userName)) {
                userName = this.TestUserName;
            }

            userName = userName.ToLowerInvariant();

            var existingTestUser = ADDataAccess.GetADObjectProperties<UserAccount>(
                "DC=contoso,DC=com", null, string.Format("samAccountName={0}", userName), PartitionScope.LocalPartition)
                .FirstOrDefault();

            if (existingTestUser != null) {
                return existingTestUser;
            }



            if (string.IsNullOrWhiteSpace(givenName)) {
                givenName = "John";
            }
            if (string.IsNullOrWhiteSpace(surname)) {
                surname = "Smith";
            }

            string displayName = string.Format("{0},{1}", surname, givenName);
            string mail = string.Format("{0}.{1}@contoso.com", givenName, surname);
            string testUserDN = string.Format("CN={0},{1}", userName, UsersTestouDN);
            string domainPlusName = string.Format(@"CONTOSO\{0}", userName);

            var testUser = ADDataAccess.GetADObjectProperties<UserAccount>(
                testUserDN, UserAccount.AttributesToGet, ADDataAccess.QueryFilters.UserAccounts, PartitionScope.LocalPartition)
                .SingleOrDefault();

            if (testUser != null) {
                ADDataAccess.Delete(testUser.DN);
            }

            ADDataAccess.UserAccountMethods.CreateUserAccount(UsersTestouDN, userName);
            testUser = ADDataAccess.GetADObjectProperties<UserAccount>(
                testUserDN, UserAccount.AttributesToGet, ADDataAccess.QueryFilters.UserAccounts, PartitionScope.LocalPartition)
                .SingleOrDefault();

            Assert.IsNotNull(testUser);
            Assert.IsTrue(testUser.IsAccountDisabled);
            ADDataAccess.UserAccountMethods.SetRandomPassword(testUser.DN);

            string description = "This is the desciption text";
            string info = "This is the info attribute/Notes field in AD Users and Computers";

            string physicalDeliveryOfficeName = "5th Floor";
            string telephoneNumber = "210-555-1234";

            string streetAddress = "678 Meadow Lane";
            string city = "Toronto";
            string state = "Ontario";
            string postalCode = "LU4 932";
            string country = "Canada";
            string countryAbbreviation = "CA";
            int countryCode = 124;

            string company = "Contoso Corporation";
            string department = "12/345/67";
            string title = "Managing Director";

            ADDataAccess.UpdateAttribute(testUser.DN, "givenName", givenName);
            ADDataAccess.UpdateAttribute(testUser.DN, "sn", surname);
            ADDataAccess.UpdateAttribute(testUser.DN, "displayName", displayName);

            // description is multivalued
            ADDataAccess.UpdateAttribute(testUser.DN, "description", description);

            // info is the Notes field in AD Users and Computers
            ADDataAccess.UpdateAttribute(testUser.DN, "info", info);

            ADDataAccess.UpdateAttribute(testUser.DN, "mail", mail);

            ADDataAccess.UpdateAttribute(testUser.DN, "physicalDeliveryOfficeName", physicalDeliveryOfficeName);
            ADDataAccess.UpdateAttribute(testUser.DN, "telephoneNumber", telephoneNumber);

            ADDataAccess.UpdateAttribute(testUser.DN, "streetAddress", streetAddress);
            ADDataAccess.UpdateAttribute(testUser.DN, "l", city);
            ADDataAccess.UpdateAttribute(testUser.DN, "st", state);
            ADDataAccess.UpdateAttribute(testUser.DN, "postalCode", postalCode);
            ADDataAccess.UpdateAttribute(testUser.DN, "c", countryAbbreviation);
            ADDataAccess.UpdateAttribute(testUser.DN, "co", country);
            ADDataAccess.UpdateAttribute(testUser.DN, "countryCode", countryCode);

            ADDataAccess.UpdateAttribute(testUser.DN, "company", company);
            ADDataAccess.UpdateAttribute(testUser.DN, "department", department);
            ADDataAccess.UpdateAttribute(testUser.DN, "title", title);

            ADDataAccess.UserAccountMethods.EnableAccount(testUser.DN);
            ADDataAccess.UserAccountMethods.RemovePasswordNotRequired(testUser.DN);

            testUser = testUser.Refresh<UserAccount>();

            Assert.AreEqual(displayName, testUser.DisplayName);
            Assert.AreEqual(givenName, testUser.GivenName);
            Assert.AreEqual(surname, testUser.Surname);
            Assert.AreEqual(description, testUser.Description);
            Assert.AreEqual(info, testUser.Info);
            Assert.AreEqual(mail, testUser.Mail);
            Assert.AreEqual(physicalDeliveryOfficeName, testUser.LocationAttributes.PhysicalDeliveryOfficeName);
            Assert.AreEqual(streetAddress, testUser.LocationAttributes.StreetAddress);
            Assert.AreEqual(city, testUser.LocationAttributes.City);
            Assert.AreEqual(state, testUser.LocationAttributes.StateAbbreviation);
            Assert.AreEqual(postalCode, testUser.LocationAttributes.PostalCode);
            Assert.AreEqual(country, testUser.LocationAttributes.Country);
            Assert.AreEqual(countryAbbreviation, testUser.LocationAttributes.CountryAbbrevation);
            Assert.AreEqual(testUser.LocationAttributes.CountryCode, countryCode);
            Assert.AreEqual(company, testUser.OrganizationAttributes.Company);
            Assert.AreEqual(department, testUser.OrganizationAttributes.Department);
            Assert.AreEqual(title, testUser.OrganizationAttributes.Title);
            Assert.AreEqual(telephoneNumber, testUser.TelephoneAttributes.TelephoneNumber);

            Assert.AreEqual(testUserDN, testUser.DN);
            Assert.AreEqual("contoso.com", testUser.DNSDomain);
            Assert.AreEqual("CONTOSO", testUser.Domain);
            Assert.AreEqual("CONTOSO", testUser.DomainCN);
            Assert.AreEqual(domainPlusName, testUser.DomainCNPlusName);
            Assert.AreEqual("DC=contoso,DC=com", testUser.DomainDN);
            Assert.AreEqual(domainPlusName, testUser.DomainPlusName);
            Assert.IsFalse(testUser.IsAccountDisabled);
            Assert.IsFalse(testUser.IsAccountLockedOut);
            Assert.IsFalse(testUser.IsAccountSensitiveAndCannotBeDelegated);
            Assert.IsFalse(testUser.IsCannotChangePassword);
            Assert.IsFalse(testUser.IsCriticalSystemObject);
            Assert.IsFalse(testUser.IsDeleted);
            Assert.IsFalse(testUser.IsPasswordExpired);
            Assert.IsFalse(testUser.IsPasswordNeverExpires);
            Assert.IsFalse(testUser.IsPasswordNotRequired);
            Assert.IsFalse(testUser.IsProtectedFromDelete);
            Assert.IsFalse(testUser.IsRecycled);
            Assert.IsFalse(testUser.IsSmartCardRequired);
            Assert.IsTrue(testUser.IsValid);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testUser.LdapDN));
            Assert.AreEqual(userName, testUser.Name);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(testUser.ObjectCategory));
            Assert.IsTrue(testUser.ObjectClass.Any(x => string.Equals(x, "user", StringComparison.OrdinalIgnoreCase)));
            Assert.IsTrue(testUser.ObjectGuid != Guid.Empty);
            Assert.IsNotNull(testUser.ObjectSid);
            Assert.IsTrue(testUser.ParentDN == UsersTestouDN);
            Assert.IsNotNull(testUser.PwdLastSet);
            Assert.IsNotNull(testUser.PwdLastSetUTC);
            Assert.IsTrue(testUser.SamAccountName == userName);
            Assert.IsTrue(testUser.SamAccountType.HasFlag(SamAccountType.SAM_USER_OBJECT));
            Assert.IsTrue(testUser.TopLevelOU == "TSTOU");
            Assert.IsTrue(testUser.TopLevelOUDN == TestouDN);
            Assert.IsTrue(testUser.UserAccountControl != UserAccountControlFlags.NotSet);
            Assert.IsTrue(testUser.WhenChanged > DateTime.UtcNow.AddDays(-1));
            Assert.IsTrue(testUser.WhenChangedUTC > DateTime.UtcNow.AddDays(-1));
            Assert.IsTrue(testUser.WhenCreated > DateTime.UtcNow.AddDays(-1));
            Assert.IsTrue(testUser.WhenCreatedUTC > DateTime.UtcNow.AddDays(-1));

            #region Custom Attributes
            ADDataAccess.UpdateAttribute(testUser.DN, "miExtensionAttribute1", "AAAAAAAAAAAAAAAA");
            ADDataAccess.UpdateAttribute(testUser.DN, "miExtensionAttribute1", "BBBBBBBBBBBBBBBB");
            ADDataAccess.UpdateAttribute(testUser.DN, "miExtensionAttribute3", "AAAAAAAAAAAAAAAA");
            ADDataAccess.UpdateAttribute(testUser.DN, "miExtensionAttribute3", "BBBBBBBBBBBBBBBB");

            testUser = testUser.Refresh<UserAccount>();

            Assert.IsTrue(testUser.CustomAttributes.MIExtensionAttribute1.Any(x => string.Equals(x, "AAAAAAAAAAAAAAAA")));
            Assert.IsTrue(testUser.CustomAttributes.MIExtensionAttribute1.Any(x => string.Equals(x, "BBBBBBBBBBBBBBBB")));
            Assert.IsTrue(testUser.CustomAttributes.MIExtensionAttribute3.Any(x => string.Equals(x, "AAAAAAAAAAAAAAAA")));
            Assert.IsTrue(testUser.CustomAttributes.MIExtensionAttribute3.Any(x => string.Equals(x, "BBBBBBBBBBBBBBBB")));
            #endregion

            return testUser;
        }

        #endregion

        #region Test Setup/Cleanup
        [TestInitialize]
        public void TestSetup() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());
            ADDataAccess.Initialize();
            Assert.IsTrue(ADDataAccess.Initialized);

            Assert.IsNotNull(ADDataAccess.GroupMethods);
            Assert.IsNotNull(ADDataAccess.UserAccountMethods);
            Assert.IsNotNull(ADDataAccess.OUMethods);

            Assert.IsTrue(ADDataAccess.ADDomainInfo.Count > 0);
            Assert.IsTrue(ADDataAccess.ADDomains.Count > 0);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(ADDataAccess.ForestRootDN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(ADDataAccess.ForestRootConfigurationNamingContext));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(ADDataAccess.ForestRootDomainNamingContext));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(ADDataAccess.ForestRootSchemaNamingContext));

            string testouFilter = string.Format("(&(objectCategory=organizationalUnit)(distinguishedName={0}))", TestouDN);
            var testOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                ADDataAccess.ForestRootDN, null, testouFilter, PartitionScope.LocalPartition)
                .SingleOrDefault();
            if (testOU == null) {
                ADDataAccess.OUMethods.CreateOU(TestouDN);
                testOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                    ADDataAccess.ForestRootDN, null, testouFilter, PartitionScope.LocalPartition)
                    .SingleOrDefault();
            }

            Assert.IsNotNull(testOU);

            string usersTestOUFilter = string.Format("(&(objectCategory=organizationalUnit)(distinguishedName={0}))", UsersTestouDN);
            var usersTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                ADDataAccess.ForestRootDN, null, usersTestOUFilter, PartitionScope.LocalPartition)
                .SingleOrDefault();
            if (usersTestOU == null) {
                ADDataAccess.OUMethods.CreateOU(UsersTestouDN);
                usersTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                    ADDataAccess.ForestRootDN, null, usersTestOUFilter, PartitionScope.LocalPartition)
                    .SingleOrDefault();
            }

            Assert.IsNotNull(usersTestOU);

            string groupsTestOUFilter = string.Format("(&(objectCategory=organizationalUnit)(distinguishedName={0}))", GroupsTestOUDN);
            var groupsTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                ADDataAccess.ForestRootDN, null, groupsTestOUFilter, PartitionScope.LocalPartition)
                .SingleOrDefault();
            if (groupsTestOU == null) {
                ADDataAccess.OUMethods.CreateOU(GroupsTestOUDN);
                usersTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                    ADDataAccess.ForestRootDN, null, groupsTestOUFilter, PartitionScope.LocalPartition)
                    .SingleOrDefault();
            }

            Assert.IsNotNull(groupsTestOU);

            string computersTestOUFilter = string.Format("(&(objectCategory=organizationalUnit)(distinguishedName={0}))", ComputersTestouDN);
            var computersTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                ADDataAccess.ForestRootDN, null, computersTestOUFilter, PartitionScope.LocalPartition)
                .SingleOrDefault();
            if (computersTestOU == null) {
                ADDataAccess.OUMethods.CreateOU(ComputersTestouDN);
                computersTestOU = ADDataAccess.GetADObjectProperties<OrganizationalUnit>(
                    ADDataAccess.ForestRootDN, null, computersTestOUFilter, PartitionScope.LocalPartition)
                    .SingleOrDefault();
            }

            Assert.IsNotNull(computersTestOU);

            DeleteTestObjects();
        }

        [TestCleanup]
        public void TestCleanup() {
            DeleteTestObjects();
        }

        private void DeleteTestObjects() {
            var testGroups = ADDataAccess.GetADObjectProperties<GroupProperties>(
                GroupsTestOUDN, null, ADDataAccess.QueryFilters.GroupFilter, PartitionScope.LocalPartition)
                .ToList();

            foreach (var group in testGroups) {
                ADDataAccess.Delete(group.DN);
            }

            var testUsers = ADDataAccess.GetADObjectProperties<UserAccount>(
                UsersTestouDN, null, ADDataAccess.QueryFilters.UserAccounts, PartitionScope.LocalPartition)
                .ToList();

            foreach (var user in testUsers) {
                ADDataAccess.Delete(user.DN);
            }

            var testComputers = ADDataAccess.GetADObjectProperties<ComputerAccount>(
                ComputersTestouDN, null, ADDataAccess.QueryFilters.ComputerAccounts, PartitionScope.LocalPartition)
                .ToList();

            foreach (var computer in testComputers) {
                ADDataAccess.Delete(computer.DN);
            }

            var testContacts = ADDataAccess.GetADObjectProperties<Contact>(
                UsersTestouDN, null, ADDataAccess.QueryFilters.Contacts, PartitionScope.LocalPartition)
                .ToList();

            foreach (var contact in testContacts) {
                ADDataAccess.Delete(contact.DN);
            }
        }
        #endregion

        [TestMethod]
        public void Test_ClearAttribute() {
            var user = GetTestUser();

            Assert.IsNotNull(user);
            Assert.IsFalse(string.IsNullOrWhiteSpace(user.Surname));

            ADDataAccess.UpdateAttribute(user.DN, "sn", "");

            user = user.Refresh<UserAccount>();

            Assert.IsNotNull(user);
            Assert.IsTrue(string.IsNullOrWhiteSpace(user.Surname));
        }

        [TestMethod]
        public void Test_UpdateAttribute() {
            var user = GetTestUser();

            Assert.IsNotNull(user);
            Assert.IsFalse(user.ExchangeBaseAttributes.MSExchHideFromAddressLists);

            ADDataAccess.UpdateAttribute(user.DN, "msExchHideFromAddressLists", true);

            user = user.Refresh<UserAccount>();

            Assert.IsNotNull(user);
            Assert.IsTrue(user.ExchangeBaseAttributes.MSExchHideFromAddressLists);
        }

        [TestMethod]
        public void Test_AppendMultiValuedAttribute() {
            var user = GetTestUser();

            Assert.IsNotNull(user);
            Assert.AreEqual(0, user.ExchangeExtensionAttributes.MSExchExtensionCustomAttribute1.Count);

            var values = new List<string>();
            values.Add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
            values.Add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
            values.Add("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC");
            values.Add("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
            values.Add("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
            values.Add("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
            values.Add("GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
            values.Add("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH");
            values.Add("IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII");
            values.Add("JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ");
            values.Add("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK");
            values.Add("LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL");
            values.Add("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
            values.Add("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
            values.Add("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO");
            values.Add("PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP");
            values.Add("QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ");
            values.Add("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR");
            values.Add("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS");
            values.Add("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT");
            values.Add("UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
            values.Add("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV");
            values.Add("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW");
            values.Add("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
            values.Add("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
            values.Add("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
            values.Add("00000000000000000000000000000000000000000000000000000000");
            values.Add("11111111111111111111111111111111111111111111111111111111");
            values.Add("22222222222222222222222222222222222222222222222222222222");
            values.Add("33333333333333333333333333333333333333333333333333333333");
            values.Add("44444444444444444444444444444444444444444444444444444444");
            values.Add("55555555555555555555555555555555555555555555555555555555");
            values.Add("66666666666666666666666666666666666666666666666666666666");
            values.Add("77777777777777777777777777777777777777777777777777777777");
            values.Add("88888888888888888888888888888888888888888888888888888888");
            values.Add("99999999999999999999999999999999999999999999999999999999");


            ADDataAccess.AppendMutliValuedAttribute(user.DN, "msExchExtensionCustomAttribute1", values.ToArray());

            user = user.Refresh<UserAccount>();

            Assert.IsNotNull(user);
            Assert.AreEqual(values.Count, user.ExchangeExtensionAttributes.MSExchExtensionCustomAttribute1.Count);

            foreach (var value in values) {
                Assert.IsTrue(user.ExchangeExtensionAttributes.MSExchExtensionCustomAttribute1.Any(x => string.Equals(value, x)));
            }

        }

        [TestMethod]
        public void Test_CreateUserGroup() {
            var user = GetTestUser();
            var globalSecurityGroup = GetTestGroup("GRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);

            Assert.IsNotNull(user);
            Assert.IsNotNull(globalSecurityGroup);

            var userGroups = ADDataAccess.GetADObjectProperties<UserGroup>(TestouDN, UserGroup.AttributesToGet, null, PartitionScope.LocalPartition);

            Assert.IsTrue(userGroups.Count > 0);
            Assert.IsTrue(userGroups.Any(x => string.Equals(x.DN, user.DN, StringComparison.OrdinalIgnoreCase)));
            Assert.IsTrue(userGroups.Any(x => string.Equals(x.DN, globalSecurityGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_CreateUserGroupComputer() {
            var user = GetTestUser();
            var group = GetTestGroup("GRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);
            var computer = GetTestComputer();

            Assert.IsNotNull(user);
            Assert.IsNotNull(group);
            Assert.IsNotNull(computer);

            var userGroupComputers = ADDataAccess.GetADObjectProperties<UserGroupComputer>(TestouDN, UserGroupComputer.AttributesToGet, null, PartitionScope.LocalPartition);

            Assert.IsTrue(userGroupComputers.Count > 0);
            Assert.IsTrue(userGroupComputers.Any(x => string.Equals(x.DN, user.DN, StringComparison.OrdinalIgnoreCase)));
            Assert.IsTrue(userGroupComputers.Any(x => string.Equals(x.DN, group.DN, StringComparison.OrdinalIgnoreCase)));
            Assert.IsTrue(userGroupComputers.Any(x => string.Equals(x.DN, computer.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_DeserializeObjectsFromLdif() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var filePath = @"C:\TEMP\export.ldif";
            if (File.Exists(filePath)) {
                File.Delete(filePath);
            }

            var testUser = GetTestUser();
            ADDataAccess.ExportObjectsToLdif(testUser.DN, filePath);

            Assert.IsTrue(File.Exists(filePath));

            var fileLines = File.ReadAllLines(filePath);
            var expectedObjectCount = fileLines.Where(line => line.StartsWith("dn:", StringComparison.OrdinalIgnoreCase)).Count();
            var deserializedObjects = ADDataAccess.DeserializeObjectsFromLdif<UserAccount>(fileLines);

            Assert.IsTrue(deserializedObjects.Count > 0);
            Assert.AreEqual(deserializedObjects.Count, expectedObjectCount);

            var deserializedUser = deserializedObjects.SingleOrDefault();

            Assert.IsNotNull(deserializedUser);
            Assert.AreEqual(testUser.DN, deserializedUser.DN);
        }

        [TestMethod]
        public void Test_FindByObjectGuid() {
            var testUser = GetTestUser();
            var foundUser = ADDataAccess.FindByObjectGuid<UserAccount>(testUser.ObjectGuid);

            Assert.IsNotNull(foundUser);
            Assert.AreEqual(foundUser.ObjectGuid, testUser.ObjectGuid);
        }

        [TestMethod]
        public void Test_GetDeletedObject() {
            var testUser = GetTestUser();
            ADDataAccess.Delete(testUser.DN);

            var users = ADDataAccess.GetADObjectProperties<UserAccount>(TestouDN, UserAccount.AttributesToGet, null, PartitionScope.LocalPartition);

            Assert.IsFalse(users.Any(x => string.Equals(x.DN, testUser.DN, StringComparison.OrdinalIgnoreCase)));

            string filter = string.Format("(&(samAccountName={0})(isDeleted=TRUE))", TestUserName);

            List<UserAccount> deletedUsers = new List<UserAccount>();
            try {
                ADDataAccess.UseTombstonedObjects = true;
                deletedUsers = ADDataAccess.GetADObjectProperties<UserAccount>(testUser.DomainDN, UserAccount.AttributesToGet, filter, PartitionScope.LocalPartition);
            }
            finally {
                ADDataAccess.UseTombstonedObjects = false;
            }

            Assert.IsTrue(deletedUsers.Count > 0);
            Assert.IsTrue(deletedUsers.Any(x => x.ObjectGuid == testUser.ObjectGuid));
        }

        [TestMethod]
        public void Test_GetDNSZone() {

            var filter = "(objectCategory=dnsZone)";
            var baseDN = "CN=MicrosoftDNS,DC=DomainDNSZones,DC=contoso,DC=com";
            // var baseDN = "CN=MicrosoftDNS,CN=System,DC=contoso,DC=com";
            var dnsZones = ADDataAccess.GetADObjectProperties<DNSZone>(baseDN, null, filter, PartitionScope.LocalPartition);

            Assert.IsTrue(dnsZones.Count > 0);
        }

        [TestMethod]
        public void Test_GetDomainDNSZoneDNSRecords() {

            var filter = "(objectCategory=dnsNode)";
            var baseDN = "CN=MicrosoftDNS,DC=DomainDnsZones,DC=contoso,DC=com";
            var dnsRecords = ADDataAccess.GetADObjectProperties<DNSResourceRecord>(baseDN, null, filter, PartitionScope.LocalPartition);

            Assert.IsTrue(dnsRecords.Count > 0);

            var allDNSRecords = new List<DNSResourceRecord>();

            foreach (var record in dnsRecords) {
                allDNSRecords.Add(record);
                foreach (var childRecord in record.ChildDNSRecords) {
                    allDNSRecords.Add(childRecord);
                }
            }

            foreach (var record in allDNSRecords) {
                switch (record.RecordType) {
                    case DNSRecordType.SOA:
                        var dnsSOARecord = new DNSSOARecord(record);
                        Assert.IsTrue(dnsSOARecord.IsValid);
                        break;

                    case DNSRecordType.SRV:
                        var dnsSRVRecord = new DNSSRVRecord(record);
                        Assert.IsTrue(dnsSRVRecord.IsValid);
                        break;

                    case DNSRecordType.NS:
                        var dnsNSRecord = new DNSNSRecord(record);
                        Assert.IsTrue(dnsNSRecord.IsValid);
                        break;

                    case DNSRecordType.A:
                        var dnsARecord = new DNSARecord(record);
                        Assert.IsTrue(dnsARecord.IsValid);
                        break;

                    case DNSRecordType.AAAA:
                        var dnsAAAARecord = new DNSAAAARecord(record);
                        Assert.IsTrue(dnsAAAARecord.IsValid);
                        break;

                    case DNSRecordType.CNAME:
                        var dnsCNAMERecord = new DNSCNAMERecord(record);
                        Assert.IsTrue(dnsCNAMERecord.IsValid);
                        break;

                    case DNSRecordType.MX:
                        var dnsMXRecord = new DNSMXRecord(record);
                        Assert.IsTrue(dnsMXRecord.IsValid);
                        break;

                    default:
                        Debug.WriteLine(string.Format("Record type not tested: {0}", record.RecordType));
                        break;
                }
            }
        }

        [TestMethod]
        public void Test_GetForestDNSZoneDNSRecords() {

            var filter = "(objectCategory=dnsNode)";
            var baseDN = "CN=MicrosoftDNS,DC=ForestDnsZones,DC=contoso,DC=com";
            var dnsRecords = ADDataAccess.GetADObjectProperties<DNSResourceRecord>(baseDN, null, filter, PartitionScope.LocalPartition);

            Assert.IsTrue(dnsRecords.Count > 0);

            var allDNSRecords = new List<DNSResourceRecord>();

            foreach (var record in dnsRecords) {
                allDNSRecords.Add(record);
                foreach (var childRecord in record.ChildDNSRecords) {
                    allDNSRecords.Add(childRecord);
                }
            }

            foreach (var record in allDNSRecords) {
                switch (record.RecordType) {
                    case DNSRecordType.SOA:
                        var dnsSOARecord = new DNSSOARecord(record);
                        Assert.IsTrue(dnsSOARecord.IsValid);
                        break;

                    case DNSRecordType.SRV:
                        var dnsSRVRecord = new DNSSRVRecord(record);
                        Assert.IsTrue(dnsSRVRecord.IsValid);
                        break;

                    case DNSRecordType.NS:
                        var dnsNSRecord = new DNSNSRecord(record);
                        Assert.IsTrue(dnsNSRecord.IsValid);
                        break;

                    case DNSRecordType.A:
                        var dnsARecord = new DNSARecord(record);
                        Assert.IsTrue(dnsARecord.IsValid);
                        break;

                    case DNSRecordType.AAAA:
                        var dnsAAAARecord = new DNSAAAARecord(record);
                        Assert.IsTrue(dnsAAAARecord.IsValid);
                        break;

                    case DNSRecordType.CNAME:
                        var dnsCNAMERecord = new DNSCNAMERecord(record);
                        Assert.IsTrue(dnsCNAMERecord.IsValid);
                        break;

                    case DNSRecordType.MX:
                        var dnsMXRecord = new DNSMXRecord(record);
                        Assert.IsTrue(dnsMXRecord.IsValid);
                        break;

                    default:
                        Debug.WriteLine(string.Format("Record type not tested: {0}", record.RecordType));
                        break;
                }
            }
        }

        [TestMethod]
        public void Test_GetDomainNetbiosName() {

            string testUserDN = "CN=JSmith,OU=Users,OU=TSTOU,DC=contoso,DC=com";
            string domainNetbiosName = ADDataAccess.GetDomainNetBiosNameFromDNOrDNSName(testUserDN);
            Assert.AreEqual(domainNetbiosName, "CONTOSO");

            string lostAndFoundDN = "LDAP://contosomddc1.contoso.com/DC=TESTPC001,CN=LostAndFound,DC=contoso,DC=com";
            domainNetbiosName = ADDataAccess.GetDomainNetBiosNameFromDNOrDNSName(lostAndFoundDN);
            Assert.AreEqual(domainNetbiosName, "CONTOSO");

            lostAndFoundDN = "CN=2013-10-07T14:37:34-05:00{DC8A3BB5-1885-4C5A-B9F4-FDD72511A1C2},CN=LostAndFound,DC=contoso,DC=com";
            domainNetbiosName = ADDataAccess.GetDomainNetBiosNameFromDNOrDNSName(lostAndFoundDN);
            Assert.AreEqual(domainNetbiosName, "CONTOSO");

            string microsoftDnsDN = "DC=c.root-servers.net,DC=RootDNSServers,CN=MicrosoftDNS,CN=System,DC=contoso,DC=com";
            domainNetbiosName = ADDataAccess.GetDomainNetBiosNameFromDNOrDNSName(microsoftDnsDN);
            Assert.AreEqual(domainNetbiosName, "CONTOSO");
        }

        [TestMethod]
        public void Test_GetReplicationMetadata() {
            var testUser = GetTestUser();

            testUser.GetReplicationMetadata();
            Assert.IsTrue(testUser.ReplicationAttributeMetadata.Count > 0);
            //Assert.IsTrue(testUser.ReplicationValueMetadata.Count > 0);

            var testDistributionGroup = GetTestGroup("GRP-TEST-GLOBALDIST", GroupTypeFlags.GlobalDistribution);
            testDistributionGroup.GetGroupMemberDNs();
            if (!testDistributionGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testDistributionGroup.DN, testUser.DN);
            }

            var groups = ADDataAccess.GroupMethods.GetDirectGroupMemberships(testUser.DN);

            Assert.IsTrue(groups.Count > 0);
            Assert.IsTrue(groups.Any(x => string.Equals(x.DN, testDistributionGroup.DN, StringComparison.OrdinalIgnoreCase)));

            testDistributionGroup.GetReplicationMetadata();

            Assert.IsTrue(testDistributionGroup.ReplicationValueMetadata.Count > 0);

            var groupMemberValueMetadata = testDistributionGroup.ReplicationValueMetadata
                .Where(x =>
                    string.Equals(x.PszAttributeName, "member", StringComparison.OrdinalIgnoreCase)
                    && string.Equals(x.PszObjectDn, testUser.DN, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault();

            Assert.IsNotNull(groupMemberValueMetadata);
        }

        [TestMethod]
        public void Test_ReplicateSingleObject() {

            var sourceDC = "contosomddc1.contoso.com";
            var targetDC = "fabrikammddc1.fabrikam.com";

            var testUserName = string.Format("Test{0}", DateTime.Now.ToString("yyMMddHHmmss"));
            var testUser = GetTestUser(testUserName, "Test", "User");
            Assert.IsNotNull(testUser);

            //var contosoDNPath = string.Format("LDAP://contosomddc1.contoso.com:389/{0}", testUser.DN);
            var fabrikamDNPath = string.Format("GC://fabrikammddc1.fabrikam.com:3268/{0}", testUser.DN);

            var authenticationTypes = AuthenticationTypes.ServerBind | AuthenticationTypes.Signing | AuthenticationTypes.Sealing;
            SearchResult searchResult = null;

            try {
                using (var directoryEntry = new DirectoryEntry(fabrikamDNPath, null, null, authenticationTypes))
                using (var directorySearcher = new DirectorySearcher(directoryEntry)) {
                    directorySearcher.SearchScope = SearchScope.Base;
                    directorySearcher.ReferralChasing = ReferralChasingOption.None;
                    searchResult = directorySearcher.FindOne();
                }
            }
            catch (DirectoryServicesCOMException e) {
                Debug.WriteLine(e.VerboseExceptionString());
                if ((e.Message.IndexOf("A referral was returned from the server", StringComparison.OrdinalIgnoreCase) == -1) 
                    && (e.Message.IndexOf("There is no such object on the server", StringComparison.OrdinalIgnoreCase) == -1)) {
                    throw;
                }
            }

            Assert.IsNull(searchResult);

            ADDataAccess.ReplicateSingleObject(targetDC, sourceDC, testUser.DN);

            try {
                using (var directoryEntry = new DirectoryEntry(fabrikamDNPath, null, null, authenticationTypes))
                using (var directorySearcher = new DirectorySearcher(directoryEntry)) {
                    directorySearcher.SearchScope = SearchScope.Base;
                    directorySearcher.ReferralChasing = ReferralChasingOption.None;
                    searchResult = directorySearcher.FindOne();
                }
            }
            catch (Exception e) {
                Debug.WriteLine(e.VerboseExceptionString());
                throw;
            }

            Assert.IsNotNull(searchResult);
        }

        [TestMethod]
        public void Test_ValidateExchangeDatabases() {

            var baseDN = string.Format("CN=Microsoft Exchange,CN=Services,{0}", ADDataAccess.ForestRootConfigurationNamingContext);
            var filter = string.Format("(objectCategory={0})", ExchangeDatabase.ObjectCategoryShortName);
            var exchangeDatabases = ADDataAccess.GetADObjectProperties<ExchangeDatabase>(
                baseDN, null, filter, PartitionScope.LocalPartition);

            foreach (var exchangeDatabase in exchangeDatabases) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.AdminDisplayName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.LegacyExchangeDN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.MSExchOwningServer));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.MSExchMasterServerOrAvailabilityGroup));
                Assert.IsTrue(exchangeDatabase.MSExchVersion != MSExchVersion.Undefined);
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.DN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.ObjectCategory));
                Assert.IsTrue(exchangeDatabase.ObjectClass.Count > 0);
                Assert.IsTrue(exchangeDatabase.ObjectGuid != Guid.Empty);
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeDatabase.ParentDN));
                Assert.IsTrue(exchangeDatabase.WhenChanged > DateTime.MinValue);
                Assert.IsTrue(exchangeDatabase.WhenChangedUTC > DateTime.MinValue);
                Assert.IsTrue(exchangeDatabase.WhenCreated > DateTime.MinValue);
                Assert.IsTrue(exchangeDatabase.WhenCreatedUTC > DateTime.MinValue);
            }
        }

        [TestMethod]
        public void Test_ValidateExchangeServers() {

            var baseDN = string.Format("CN=Microsoft Exchange,CN=Services,{0}", ADDataAccess.ForestRootConfigurationNamingContext);
            var filter = string.Format("(objectCategory={0})", ExchangeServer.ObjectCategoryShortName);
            var exchangeServers = ADDataAccess.GetADObjectProperties<ExchangeServer>(
                baseDN, null, filter, PartitionScope.LocalPartition);

            foreach (var exchangeServer in exchangeServers) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeServer.AdminDisplayName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeServer.LegacyExchangeDN));
                Assert.AreNotEqual(exchangeServer.MSExchCurrentServerRoles, MSExchCurrentServerRoles.Undefined);
                Assert.IsTrue(exchangeServer.MSExchVersion != MSExchVersion.Undefined);
                Assert.IsTrue(exchangeServer.SerialNumber.Count > 0);

                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeServer.DN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeServer.ObjectCategory));
                Assert.IsTrue(exchangeServer.ObjectClass.Count > 0);
                Assert.IsTrue(exchangeServer.ObjectGuid != Guid.Empty);
                Assert.IsTrue(!string.IsNullOrWhiteSpace(exchangeServer.ParentDN));
                Assert.IsTrue(exchangeServer.WhenChanged > DateTime.MinValue);
                Assert.IsTrue(exchangeServer.WhenChangedUTC > DateTime.MinValue);
                Assert.IsTrue(exchangeServer.WhenCreated > DateTime.MinValue);
                Assert.IsTrue(exchangeServer.WhenCreatedUTC > DateTime.MinValue);
            }
        }

        [TestMethod]
        public void Test_ValidateOUs() {
            var ous = ADDataAccess.GetAllADObjectProperties<OrganizationalUnit>(OrganizationalUnit.AttributesToGet);

            Assert.IsTrue(ous.Count > 0);

            foreach (var ou in ous) {
                if (string.Equals(ou.Name, "Domain Controllers", StringComparison.OrdinalIgnoreCase)) {
                    Assert.IsTrue(ou.IsTopLevel);
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.GPLink));
                }

                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.DN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.DNSDomain));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.Domain));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.DomainCN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.DomainDN));
                Assert.IsTrue(ou.IsValid);
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.Name));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.ObjectCategory));
                Assert.IsTrue(ou.ObjectClass.Count > 0);
                Assert.IsTrue(ou.ObjectClass.Any(x => string.Equals(x, "organizationalUnit", StringComparison.OrdinalIgnoreCase)));
                Assert.IsTrue(ou.ObjectGuid != Guid.Empty);
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.Name));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.ParentDN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.TopLevelOU));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(ou.TopLevelOUDN));
                Assert.IsTrue(ou.WhenChanged > DateTime.MinValue);
                Assert.IsTrue(ou.WhenChangedUTC > DateTime.MinValue);
                Assert.IsTrue(ou.WhenCreated > DateTime.MinValue);
                Assert.IsTrue(ou.WhenCreatedUTC > DateTime.MinValue);

            }
        }

        #region Computer Accounts

        [TestMethod]
        public void Test_CreateComputer() {
            var computer = GetTestComputer();
            Assert.IsNotNull(computer);
        }

        [TestMethod]
        public void Test_DisableComputerAccount() {
            var computer = GetTestComputer();

            Assert.IsNotNull(computer);
            Assert.IsFalse(computer.IsAccountDisabled);

            ADDataAccess.ComputerAccountMethods.DisableAccount(computer.DN);

            computer = computer.Refresh<ComputerAccount>();
            Assert.IsTrue(computer.IsAccountDisabled);
        }

        [TestMethod]
        public void Test_EnableComputerAccount() {
            var computer = GetTestComputer();

            Assert.IsNotNull(computer);
            Assert.IsFalse(computer.IsAccountDisabled);

            ADDataAccess.ComputerAccountMethods.DisableAccount(computer.DN);

            computer = computer.Refresh<ComputerAccount>();
            Assert.IsTrue(computer.IsAccountDisabled);

            ADDataAccess.ComputerAccountMethods.EnableAccount(computer.DN);

            computer = computer.Refresh<ComputerAccount>();
            Assert.IsFalse(computer.IsAccountDisabled);
        }

        [TestMethod]
        public void Test_ValidateComputerFVERecoveryInfo() {
            var computers = ADDataAccess.GetAllADObjectProperties<ComputerAccount>();

            foreach (var computer in computers) {
                if (computer.FVERecoveryInfoVolumes.Count > 0) {
                    foreach (var volume in computer.FVERecoveryInfoVolumes) {
                        Assert.IsTrue(volume.IsValid);
                        Assert.IsNotNull(volume.MSFVEKeyPackage);
                        Assert.IsTrue(volume.MSFVERecoveryGuid != Guid.Empty);
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(volume.MSFVERecoveryPassword));
                        Assert.IsTrue(volume.MSFVEVolumeGuid != Guid.Empty);
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(volume.DN));
                        Assert.IsTrue(volume.ObjectGuid != Guid.Empty);
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(volume.ObjectCategory));
                        Assert.IsTrue(volume.ObjectClass.Count > 0);
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(volume.ParentDN));
                        Assert.IsTrue(string.Equals(volume.ParentDN, computer.DN, StringComparison.OrdinalIgnoreCase));
                    }
                }
            }
        }

        [TestMethod]
        public void Test_ValidateDomainControllerComputerAccounts() {
            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                foreach (var domainDC in domainInfo.DomainDCs) {
                    var dcName = domainDC.Substring(0, domainDC.IndexOf("."));
                    string filter = string.Format("(&(objectCategory=computer)(samAccountName={0}$))", dcName);
                    var computerAccount = ADDataAccess.GetADObjectProperties<ComputerAccount>(
                        domainInfo.DistinguishedName, ComputerAccount.AttributesToGet, filter, PartitionScope.LocalPartition)
                        .SingleOrDefault();

                    Assert.IsNotNull(computerAccount);
                    Assert.IsTrue(computerAccount.IsCriticalSystemObject);

                    if (!computerAccount.MSDSIsRODC) {
                        Assert.IsTrue(computerAccount.IsTrustedForDelegation);
                        Assert.IsTrue(computerAccount.UserAccountControl.HasFlag(UserAccountControlFlags.ServerTrustAccount));
                        Assert.IsTrue(computerAccount.UserAccountControl.HasFlag(UserAccountControlFlags.TrustedForDelegation));
                    }
                    else {
                        Assert.IsTrue(computerAccount.UserAccountControl.HasFlag(UserAccountControlFlags.WorkstationTrustAccount));
                        Assert.IsTrue(computerAccount.UserAccountControl.HasFlag(UserAccountControlFlags.PartialSecretsAccount));
                        Assert.IsTrue(computerAccount.UserAccountControl.HasFlag(UserAccountControlFlags.TrustedToAuthenticateForDelegation));
                    }

                    Assert.IsTrue(!string.IsNullOrWhiteSpace(computerAccount.OperatingSystem));
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(computerAccount.OperatingSystemVersion));

                    Assert.IsNotNull(computerAccount.ObjectSid);
                    Assert.IsTrue(computerAccount.ObjectGuid != Guid.Empty);

                    Assert.IsTrue(computerAccount.SamAccountType.HasFlag(SamAccountType.SAM_MACHINE_ACCOUNT));

                    Assert.IsTrue(computerAccount.PwdLastSet > DateTime.MinValue);
                    Assert.IsTrue(computerAccount.LastLogonTimeStamp > DateTime.MinValue);
                }
            }
        }
        #endregion

        #region User Accounts

        [TestMethod]
        public void Test_CreateUserAccount() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            Assert.IsNotNull(testUser);
        }

        [TestMethod]
        public void Test_CreateUserAccountAccessRules() {

            var testUser = GetTestUser();
            Assert.IsNotNull(testUser);

            // examples of how ACE's are specified
            ActiveDirectoryAccessRule accessRule = new ActiveDirectoryAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
            ActiveDirectoryRights.ExtendedRight, AccessControlType.Allow, ExtendedRights.UserChangePassword);
            ADDataAccess.RemoveAccessRuleSpecfic(testUser.DN, accessRule);

            accessRule = new ActiveDirectoryAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
                ActiveDirectoryRights.ExtendedRight, AccessControlType.Deny, ExtendedRights.UserChangePassword);
            ADDataAccess.AddAccessRule(testUser.DN, accessRule);

            accessRule = new ActiveDirectoryAccessRule(new SecurityIdentifier(WellKnownSidType.SelfSid, null),
                ActiveDirectoryRights.ExtendedRight, AccessControlType.Allow, ExtendedRights.UserChangePassword);
            ADDataAccess.RemoveAccessRuleSpecfic(testUser.DN, accessRule);

            accessRule = new ActiveDirectoryAccessRule(new SecurityIdentifier(WellKnownSidType.SelfSid, null),
                ActiveDirectoryRights.ExtendedRight, AccessControlType.Deny, ExtendedRights.UserChangePassword);
            ADDataAccess.AddAccessRule(testUser.DN, accessRule);
        }

        [TestMethod]
        public void Test_DisableUserAccount() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            ADDataAccess.UserAccountMethods.DisableAccount(testUser.DN);
            testUser = testUser.Refresh<UserAccount>();
            Assert.IsTrue(testUser.IsAccountDisabled);
        }

        [TestMethod]
        public void Test_EnableUserAccount() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            Assert.IsNotNull(testUser);
            Assert.IsFalse(testUser.IsAccountDisabled);

            ADDataAccess.UserAccountMethods.DisableAccount(testUser.DN);
            testUser = testUser.Refresh<UserAccount>();
            Assert.IsTrue(testUser.IsAccountDisabled);

            ADDataAccess.UserAccountMethods.EnableAccount(testUser.DN);
            testUser = testUser.Refresh<UserAccount>();
            Assert.IsFalse(testUser.IsAccountDisabled);
        }

        [TestMethod]
        public void Test_GetUserFromGC() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();

            var filter = string.Format("distinguishedName={0}", testUser.DN);
            var attributesToGetGC = UserAccount.AttributesToGet
                .Where(x =>
                    ADDataAccess.ADSchemaAttributes.ContainsKey(x)
                    && ADDataAccess.ADSchemaAttributes[x].IsMemberOfPartialAttributeSet.HasValue
                    && ADDataAccess.ADSchemaAttributes[x].IsMemberOfPartialAttributeSet.Value == true)
                .ToArray();

            var user = ADDataAccess.GetADObjectProperties<UserAccount>(
                testUser.DN, attributesToGetGC, filter, PartitionScope.GlobalCatalog)
                .SingleOrDefault();

            Assert.IsNotNull(user);

            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.SamAccountName));
            Assert.AreNotEqual(user.ObjectGuid, Guid.Empty);
            Assert.IsTrue(user.SamAccountType.HasFlag(SamAccountType.SAM_USER_OBJECT));
            Assert.IsTrue(user.WhenCreated > DateTime.MinValue);
            Assert.IsTrue(user.WhenChanged > DateTime.MinValue);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.Domain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DNSDomain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainCN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainDN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainPlusName));

            Assert.IsNotNull(user.CustomAttributes);
            Assert.IsNotNull(user.ExchangeMailboxAttributes);
            Assert.IsNotNull(user.ExchangeBaseAttributes);
            Assert.IsNotNull(user.ExchangeExtensionAttributes);

            Assert.IsTrue(user.IsValid);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void Test_GetUserFromGCWithAllAttributesThrowsException() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();

            var filter = string.Format("distinguishedName={0}", testUser.DN);

            var user = ADDataAccess.GetADObjectProperties<UserAccount>(
                testUser.DN, UserAccount.AttributesToGet, filter, PartitionScope.GlobalCatalog)
                .SingleOrDefault();
        }

        [TestMethod]
        public void Test_GetUserFromLocalPartition() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();

            var filter = string.Format("distinguishedName={0}", testUser.DN);
            var user = ADDataAccess.GetADObjectProperties<UserAccount>(
                ADDataAccess.ForestRootDN, UserAccount.AttributesToGet, filter, PartitionScope.LocalPartition)
                .SingleOrDefault();

            Assert.IsNotNull(user);

            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.SamAccountName));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.ObjectCategory));
            Assert.IsTrue(user.ObjectClass.Count > 0);
            Assert.AreNotEqual(user.ObjectGuid, Guid.Empty);
            Assert.IsTrue(user.SamAccountType.HasFlag(SamAccountType.SAM_USER_OBJECT));
            Assert.IsTrue(user.WhenCreated > DateTime.MinValue);
            Assert.IsTrue(user.WhenChanged > DateTime.MinValue);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.Domain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DNSDomain));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainCN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainDN));
            Assert.IsTrue(!string.IsNullOrWhiteSpace(user.DomainPlusName));

            Assert.IsNotNull(user.CustomAttributes);
            Assert.IsNotNull(user.ExchangeMailboxAttributes);
            Assert.IsNotNull(user.ExchangeBaseAttributes);
            Assert.IsNotNull(user.ExchangeExtensionAttributes);

            Assert.IsNotNull(user.PwdLastSet);

            Assert.IsTrue(user.IsValid);
        }

        [TestMethod]
        public void Test_UserAccountManagerBacklink() {
            var testUser = GetTestUser();
            var testUserDirectReport = GetTestUser("jdoe001", "John", "Doe");

            ADDataAccess.UpdateAttribute(testUserDirectReport.DN, "manager", testUser.DN);

            testUserDirectReport = testUserDirectReport.Refresh<UserAccount>();

            Assert.AreEqual(testUserDirectReport.OrganizationAttributes.Manager, testUser.DN);

            testUser = testUser.Refresh<UserAccount>();

            Assert.IsTrue(testUser.OrganizationAttributes.DirectReports.Any(x => string.Equals(x, testUserDirectReport.DN)));
        }

        [TestMethod]
        public void Test_MSExchRemoteRecipientType() {
            var testUser = GetTestUser();

            ADDataAccess.UpdateAttribute(testUser.DN, "msExchRemoteRecipientType", (int)MSExchRemoteRecipientType.Migrated);
            testUser = ADDataAccess.GetADObjectProperties<UserAccount>(testUser.DN, null, null, PartitionScope.LocalPartition).FirstOrDefault();
            Assert.AreEqual(MSExchRemoteRecipientType.Migrated, testUser.ExchangeMailboxAttributes.MSExchRemoteRecipientType);

            ADDataAccess.UpdateAttribute(testUser.DN, "msExchRemoteRecipientType", (int)MSExchRemoteRecipientType.ProvisionMailbox);
            testUser = ADDataAccess.GetADObjectProperties<UserAccount>(testUser.DN, null, null, PartitionScope.LocalPartition).FirstOrDefault();
            Assert.AreEqual(MSExchRemoteRecipientType.ProvisionMailbox, testUser.ExchangeMailboxAttributes.MSExchRemoteRecipientType);
        }
        #endregion

        #region Contacts
        [TestMethod]
        public void Test_CreateContact() {

            var contactCN = "Jane Doe";
            var contactDisplayName = "Doe, Jane";
            var contactGivenName = "Jane";
            var contactMail = "jdoe001@contoso.com";
            var contactMailNickname = "jdoe001";
            var contactSn = "Doe";
            var contactTargetAddress = "jdoe001@gmail.com";

            var showInAddressBook = new List<string>();
            showInAddressBook.Add("CN=All Contacts(VLV),CN=All System Address Lists,CN=Address Lists Container,CN=Contoso Corporation,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com");
            showInAddressBook.Add("CN=All Contacts,CN=All Address Lists,CN=Address Lists Container,CN=Contoso Corporation,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com");
            showInAddressBook.Add("CN=All Recipients(VLV),CN=All System Address Lists,CN=Address Lists Container,CN=Contoso Corporation,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com");
            showInAddressBook.Add("CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Container,CN=Contoso Corporation,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com");

            string legacyExchangeDN = "/o=Contoso Corporation/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Jane Doe";
            var contactDN = ADDataAccess.ContactMethods.CreateMailEnabledContact(
                "OU=Users,OU=TSTOU,DC=contoso,DC=com", contactCN, contactDisplayName, contactGivenName, legacyExchangeDN,
                contactMail, contactMailNickname, contactSn, contactTargetAddress, showInAddressBook);

            Assert.IsTrue(!string.IsNullOrWhiteSpace(contactDN));

            var contact = ADDataAccess.GetAllADObjectProperties<Contact>(filter: string.Format("distinguishedName={0}", contactDN))
                .SingleOrDefault();

            Assert.IsNotNull(contact);

            Assert.AreEqual(contact.DN, contactDN);
            Assert.AreEqual(contact.CN, contactCN);
            Assert.AreEqual(contact.DisplayName, contactDisplayName);
            Assert.AreEqual(contact.GivenName, contactGivenName);
            Assert.AreEqual(contact.ExchangeBaseAttributes.LegacyExchangeDN, legacyExchangeDN);
            Assert.AreEqual(contact.Mail, contactMail);
            Assert.AreEqual(contact.ExchangeBaseAttributes.MailNickname, contactMailNickname);
            Assert.AreEqual(contact.Surname, contactSn);
            Assert.AreEqual(contact.ExchangeBaseAttributes.TargetAddress, contactTargetAddress);
            Assert.AreEqual(contact.ExchangeBaseAttributes.ShowInAddressBook.Count, showInAddressBook.Count);

            for (int index = 0; index < showInAddressBook.Count; index++) {
                Assert.IsTrue(contact.ExchangeBaseAttributes.ShowInAddressBook.Any(x =>
                    string.Equals(x, showInAddressBook[index])));
            }
        }
        #endregion

        #region Groups/Membership
        [TestMethod]
        public void Test_GetAllGroupMemberships() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("UGRP-TEST-UNIDIST", GroupTypeFlags.UniversalDistribution);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
            }
            var groups = ADDataAccess.GroupMethods.GetAllGroupMemberships(testUser.DN);

            Assert.IsTrue(groups.Any(x => string.Equals(x.DN, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetChildGroupDNs() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testGroup = GetTestGroup("UGRP-TEST-UNISEC", GroupTypeFlags.UniversalSecurity);
            var testChildGroup = GetTestGroup("GRP-TESTCHILD-GLOBALSEC", GroupTypeFlags.GlobalSecurity);

            ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testChildGroup.DN);

            var childGroupDNs = ADDataAccess.GroupMethods.GetChildGroupDNs(testGroup.DN);

            Assert.IsTrue(childGroupDNs.ContainsKey(testChildGroup.DN));
        }

        [TestMethod]
        public void Test_GetChildGroupDNsAsync() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testGroup = GetTestGroup("GRP-TEST-UNISEC", GroupTypeFlags.UniversalSecurity);
            var testChildGroup = GetTestGroup("GRP-TESTCHILD-GLOBALSEC", GroupTypeFlags.GlobalSecurity);

            ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testChildGroup.DN);

            var task = ADDataAccess.GroupMethods.GetChildGroupDNsAsync(testGroup.DN);
            task.Wait(TimeSpan.FromMinutes(10));
            Assert.IsTrue(task.IsCompleted);
            Assert.IsFalse(task.IsFaulted);

            var childGroupDNs = task.Result;

            Assert.IsTrue(childGroupDNs.ContainsKey(testChildGroup.DN));

        }

        [TestMethod]
        public void Test_GetDomainSecurityGroupMemberships() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("GRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
            }

            var securityGroupSids = ADDataAccess.GroupMethods.GetTokenGroupsSIDs(testUser.DN);

            Assert.IsTrue(securityGroupSids.Any(x => x.AccountDomainSid != null));
            Assert.IsTrue(securityGroupSids.Any(x => x.Value == testGroup.ObjectSid.Value));
        }

        [TestMethod]
        public void Test_GetDirectGroupMemberships_GLobalSecurity() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("GRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
            }

            var groups = ADDataAccess.GroupMethods.GetDirectGroupMemberships(testUser.DN);

            Assert.IsTrue(groups.Count > 0);
            Assert.IsTrue(groups.Any(x => string.Equals(x.DN, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetDirectGroupMemberships_UniversalDistribution() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("UGRP-TEST-UNIDIST", GroupTypeFlags.UniversalDistribution);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
            }

            var groups = ADDataAccess.GroupMethods.GetDirectGroupMemberships(testUser.DN);

            Assert.IsTrue(groups.Count > 0);
            Assert.IsTrue(groups.Any(x => string.Equals(x.DN, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetGroupMemberDetailsAsync() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testGroup = GetTestGroup("GRP-TEST-LOCALSEC", GroupTypeFlags.LocalSecurity);
            var testChildGroup = GetTestGroup("GRP-TESTCHILD-UNISEC", GroupTypeFlags.UniversalSecurity);

            ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testChildGroup.DN);

            var task = ADDataAccess.GroupMethods.GetGroupMemberDetailsAsync<UserGroupComputer>(testGroup.DN, UserGroupComputer.AttributesToGet);

            task.Wait(TimeSpan.FromMinutes(60));

            Assert.IsTrue(task.IsCompleted);
            Assert.IsTrue(!task.IsFaulted);

            var groupMembers = task.Result;

            Assert.IsTrue(groupMembers.ContainsKey(testChildGroup.DN));
            Assert.IsTrue(groupMembers[testChildGroup.DN].ObjectClass.Any(x => string.Equals(x, "group", StringComparison.OrdinalIgnoreCase)));

        }

        [TestMethod]
        public void Test_GetGroupMemberDNs() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            Assert.IsNotNull(testUser);

            var testGroup = GetTestGroup("GRP-TEST-LOCALSEC", GroupTypeFlags.LocalSecurity);
            var testChildGroup = GetTestGroup("UGRP-TESTCHILD-UNISEC", GroupTypeFlags.UniversalSecurity);

            ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testChildGroup.DN);
            ADDataAccess.GroupMethods.AddMemberToGroup(testChildGroup.DN, testUser.DN);

            var groupMemberDNs = ADDataAccess.GroupMethods.GetGroupMemberDNs(testGroup.DN, null, true);

            Assert.IsNotNull(groupMemberDNs);
            Assert.AreEqual(2, groupMemberDNs.Count);
            Assert.IsTrue(groupMemberDNs.ContainsKey(testUser.DN));

        }

        [TestMethod]
        public void Test_GetMemberOfAttribute_HQUsers() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser("greg");
            var testGroup = GetTestGroup("HQ Users", GroupTypeFlags.GlobalSecurity);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
                testGroup.GetGroupMemberDNs();
            }

            Assert.IsTrue(testGroup.Member.ContainsKey(testUser.DN));

            var groupDNs = ADDataAccess.GroupMethods.GetMemberOf(testUser.DN);

            Assert.IsTrue(groupDNs.Count > 0);
            Assert.IsTrue(groupDNs.Any(x => string.Equals(x, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetMemberOfAttribute_GlobalSecurity() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("GRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
                testGroup.GetGroupMemberDNs();
            }

            Assert.IsTrue(testGroup.Member.ContainsKey(testUser.DN));

            var groupDNs = ADDataAccess.GroupMethods.GetMemberOf(testUser.DN);

            Assert.IsTrue(groupDNs.Count > 0);
            Assert.IsTrue(groupDNs.Any(x => string.Equals(x, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetMemberOfAttribute_UniversalDistribution() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroup = GetTestGroup("UGRP-TEST-UNIDIST", GroupTypeFlags.UniversalDistribution);
            testGroup.GetGroupMemberDNs();
            if (!testGroup.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroup.DN, testUser.DN);
                testGroup.GetGroupMemberDNs();
            }

            Assert.IsTrue(testGroup.Member.ContainsKey(testUser.DN));

            var groupDNs = ADDataAccess.GroupMethods.GetMemberOf(testUser.DN);

            Assert.IsTrue(groupDNs.Count > 0);
            Assert.IsTrue(groupDNs.Any(x => string.Equals(x, testGroup.DN, StringComparison.OrdinalIgnoreCase)));
        }

        [TestMethod]
        public void Test_GetTokenGroupSIDs() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();
            var testGroupUniversalSecurity = GetTestGroup("UGRP-TEST-UNISEC", GroupTypeFlags.UniversalSecurity);
            testGroupUniversalSecurity.GetGroupMemberDNs();
            if (!testGroupUniversalSecurity.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroupUniversalSecurity.DN, testUser.DN);
                testGroupUniversalSecurity.GetGroupMemberDNs();
            }

            Assert.IsTrue(testGroupUniversalSecurity.Member.ContainsKey(testUser.DN));

            var testGroupGlobalSecurity = GetTestGroup("UGRP-TEST-GLOBALSEC", GroupTypeFlags.GlobalSecurity);
            testGroupGlobalSecurity.GetGroupMemberDNs();
            if (!testGroupGlobalSecurity.Member.ContainsKey(testUser.DN)) {
                ADDataAccess.GroupMethods.AddMemberToGroup(testGroupGlobalSecurity.DN, testUser.DN);
                testGroupGlobalSecurity.GetGroupMemberDNs();
            }

            Assert.IsTrue(testGroupGlobalSecurity.Member.ContainsKey(testUser.DN));

            var tokenGroupSIDS = ADDataAccess.GroupMethods.GetTokenGroupsSIDs(testUser.Domain, testUser.SamAccountName);

            Assert.IsNotNull(tokenGroupSIDS);
            Assert.IsTrue(tokenGroupSIDS.Length > 0);
            Assert.IsTrue(tokenGroupSIDS.Any(x => x.Value == testGroupUniversalSecurity.ObjectSid.Value));
            Assert.IsTrue(tokenGroupSIDS.Any(x => x.Value == testGroupGlobalSecurity.ObjectSid.Value));
        }

        #endregion

        #region Collection Validation
        [TestMethod]
        public void Test_ValidateDomainInfo() {

            Assert.IsTrue(ADDataAccess.ADDomainInfo.Count > 0);
            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.DistinguishedName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.DNSName));
                Assert.IsTrue(domainInfo.DomainDCs.Count > 0);
                Assert.AreNotEqual(domainInfo.DomainGuid, Guid.Empty);
                if (domainInfo.IsRootDomain) {
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.DomainNamingMasterOwner));
                }
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.ForestName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.InfrastractureManagerDnsName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.InfrastractureManagerOwner));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.NetbiosName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.PDCEmulatorDnsName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.PDCEmulatorOwner));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.RIDManagerDnsName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.RIDManagerOwner));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.RIDManagerDnsName));
                if (domainInfo.IsRootDomain) {
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.SchemaMasterOwner));
                }
            }
        }

        [TestMethod]
        public void Test_ValidateSchemaAttributes() {

            Assert.IsTrue(ADDataAccess.ADSchemaAttributes.Count > 0);
            foreach (var attribute in ADDataAccess.ADSchemaAttributes) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(attribute.Value.AdminDisplayName));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(attribute.Value.LDAPDisplayName));
            }
        }
        #endregion

        #region Access Rules
        [TestMethod]
        public void Test_GetSecurityDesriptor() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            var testUser = GetTestUser();

            testUser.GetObjectSecurity();

            Assert.IsNotNull(testUser.ObjectSecurity);
            Assert.IsNotNull(testUser.AccessRules);
            Assert.IsTrue(testUser.AccessRules.Count > 0);

            foreach (ActiveDirectoryAccessRule accessRule in testUser.AccessRules) {
                Assert.IsNotNull(accessRule);
            }
        }

        [TestMethod]
        public void Test_ProtectedFromDelete() {
            var testUser = GetTestUser();

            Assert.IsFalse(ADDataAccess.IsProtectedFromDelete(testUser.DN));
            ADDataAccess.AddAccessRule(testUser.DN, ADDataAccess.ProtectedFromDeleteAccessRule);

            Assert.IsTrue(ADDataAccess.IsProtectedFromDelete(testUser.DN));
        }

        [TestMethod]
        public void Test_AddUnlockAccountAccessRule() {

            Debug.WriteLine(string.Format("ADDomainInfo Count: {0}", ADDataAccess.ADDomainInfo.Count));
            Debug.WriteLine(string.Format("ADDomains Count: {0}", ADDataAccess.ADDomains.Count));

            var testUserTarget = GetTestUser("uatarget", "ua", "target");
            var testUserTrustee = GetTestUser("uatrustee", "ua", "trustee");

            Assert.IsFalse(ADDataAccess.UserAccountMethods.HasAccountUnlockAccessRule(testUserTarget.AccessRules, testUserTrustee.ObjectSid));
            ADDataAccess.UserAccountMethods.AddAccountUnlockAccessRule(testUserTarget.DN, testUserTrustee.ObjectSid);

            var accessRules = ADDataAccess.GetAccessRules(testUserTarget.DN);
            Assert.IsTrue(ADDataAccess.UserAccountMethods.HasAccountUnlockAccessRule(accessRules, testUserTrustee.ObjectSid));
        }
        #endregion

        #region Sites
        /// <summary>
        /// It is possible the Site could be empty.  Use a known good test environmetn with a site that is populated.
        /// </summary>
        [TestMethod]
        public void Test_GetSites() {
            var sites = ADDataAccess.GetADObjectProperties<Site>(
                ADDataAccess.ForestRootConfigurationNamingContext, Site.AttributesToGet, null, PartitionScope.LocalPartition);

            Assert.IsTrue(sites.Count > 0);
            foreach (var site in sites) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(site.Name));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(site.DN));
                Assert.IsTrue(!string.IsNullOrWhiteSpace(site.CN));
                Assert.IsTrue(site.WhenChanged > DateTime.MinValue);
                Assert.IsTrue(site.WhenChangedUTC > DateTime.MinValue);
                Assert.IsTrue(site.WhenCreated > DateTime.MinValue);
                Assert.IsTrue(site.WhenCreatedUTC > DateTime.MinValue);

                if (site.NTDSSiteSettings != null) {
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(site.NTDSSiteSettings.DN));
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(site.NTDSSiteSettings.CN));
                    Assert.IsTrue(site.NTDSSiteSettings.WhenChanged > DateTime.MinValue);
                    Assert.IsTrue(site.NTDSSiteSettings.WhenChangedUTC > DateTime.MinValue);
                    Assert.IsTrue(site.NTDSSiteSettings.WhenCreated > DateTime.MinValue);
                    Assert.IsTrue(site.NTDSSiteSettings.WhenCreatedUTC > DateTime.MinValue);
                }

                if (site.SiteServers.Count > 0) {
                    foreach (var server in site.SiteServers) {
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(server.DnsHostName));
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(server.ServerReference));
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(server.DN));
                        Assert.IsTrue(!string.IsNullOrWhiteSpace(server.CN));
                        Assert.IsTrue(server.WhenChanged > DateTime.MinValue);
                        Assert.IsTrue(server.WhenChangedUTC > DateTime.MinValue);
                        Assert.IsTrue(server.WhenCreated > DateTime.MinValue);
                        Assert.IsTrue(server.WhenCreatedUTC > DateTime.MinValue);

                        if (server.NTDSSettings != null) {
                            foreach (var connection in server.NTDSSettings.NTDSConnections) {
                                Assert.IsTrue(!string.IsNullOrWhiteSpace(connection.Name));
                                Assert.IsTrue(!string.IsNullOrWhiteSpace(connection.FromServer));
                                Assert.IsTrue(!string.IsNullOrWhiteSpace(connection.DN));
                                Assert.IsTrue(!string.IsNullOrWhiteSpace(connection.CN));
                                Assert.IsTrue(connection.WhenChanged > DateTime.MinValue);
                                Assert.IsTrue(connection.WhenChangedUTC > DateTime.MinValue);
                                Assert.IsTrue(connection.WhenCreated > DateTime.MinValue);
                                Assert.IsTrue(connection.WhenCreatedUTC > DateTime.MinValue);
                            }
                        }
                    }
                }
            }
        }

        [TestMethod]
        public void Test_CreateSiteLinkBridge() {

            var siteLinkBridgeName = "SiteLinkBridge-Test";
            var forestName = "contoso.com";

            var forestContext = new DirectoryContext(DirectoryContextType.Forest, forestName);
            var adTransport = ActiveDirectoryTransportType.Rpc;

            ActiveDirectorySiteLinkBridge siteLinkBridge = null;
            try {
                siteLinkBridge = ActiveDirectorySiteLinkBridge.FindByName(forestContext, siteLinkBridgeName, adTransport);
                if (siteLinkBridge != null) {
                    siteLinkBridge.Delete();
                }
            }
            catch (ActiveDirectoryObjectNotFoundException) {

            }

            ADDataAccess.SiteMethods.CreateSiteLinkBridge(forestName, siteLinkBridgeName, "CONTOSOMD-BlackHole", "CONTOSOMD-CONTOSOLA");

            siteLinkBridge = ActiveDirectorySiteLinkBridge.FindByName(forestContext, siteLinkBridgeName, adTransport);
            Assert.IsNotNull(siteLinkBridge);
            Assert.AreEqual(2, siteLinkBridge.SiteLinks.Count);

            var siteLinkBridgeCustomObject = ADDataAccess.GetADObjectProperties<SiteLinkBridge>(
                ADDataAccess.ForestRootConfigurationNamingContext, null, "(name=SiteLinkBridge-Test)", PartitionScope.LocalPartition)
                .FirstOrDefault();

            Assert.IsNotNull(siteLinkBridgeCustomObject);

            string description = string.Format("Created: {0} (UTC)", DateTime.UtcNow.YMDHMSFriendly());
            string[] values = new string[] { description };
            ADDataAccess.ReplaceMutliValuedAttribute(siteLinkBridgeCustomObject.DN, "description", values);

            siteLinkBridgeCustomObject = siteLinkBridgeCustomObject.Refresh<SiteLinkBridge>();
            Assert.AreEqual(description, siteLinkBridgeCustomObject.Description);

            siteLinkBridge.Delete();

            siteLinkBridge = null;

            try {
                siteLinkBridge = ActiveDirectorySiteLinkBridge.FindByName(forestContext, siteLinkBridgeName, adTransport);
            }
            catch (ActiveDirectoryObjectNotFoundException) {

            }
            Assert.IsNull(siteLinkBridge);

        }
        #endregion

        #region FSMO Role Owners
        [TestMethod]
        public void Test_GetInfrastractureManagerFSMORoleOwner() {

            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.InfrastractureManagerOwner));
            }
        }

        [TestMethod]
        public void Test_GetPDCEmulatorFSMORoleOwner() {

            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.PDCEmulatorOwner));
            }
        }

        [TestMethod]
        public void Test_GetRIDManagerFSMORoleOwner() {

            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                Assert.IsTrue(!string.IsNullOrWhiteSpace(domainInfo.RIDManagerOwner));
            }
        }

        [TestMethod]
        public void Test_GetDomainNamingaMasterFSMORoleOwner() {

            var domainNamingMaster = string.Empty;
            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                if (!string.IsNullOrWhiteSpace(domainInfo.DomainNamingMasterOwner)) {
                    domainNamingMaster = domainInfo.SchemaMasterOwner;
                    break;
                }
            }

            Assert.IsTrue(!string.IsNullOrWhiteSpace(domainNamingMaster));
        }

        [TestMethod]
        public void Test_GetSchemaMasterFSMORoleOwner() {

            var schemaMaster = string.Empty;
            foreach (var domainInfo in ADDataAccess.ADDomainInfo) {
                if (!string.IsNullOrWhiteSpace(domainInfo.SchemaMasterOwner)) {
                    schemaMaster = domainInfo.SchemaMasterOwner;
                    break;
                }
            }

            Assert.IsTrue(!string.IsNullOrWhiteSpace(schemaMaster));
        }
        #endregion

    }
}
